package com.bitbakery.plugin.cl.lang;

import com.bitbakery.plugin.cl.lang.lexer.CLParsingLexer;
import com.bitbakery.plugin.cl.lang.lexer.CLTokenTypes;
import com.bitbakery.plugin.cl.lang.parser.CLParser;
import com.bitbakery.plugin.cl.lang.psi.*;
import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.intellij.lang.ASTNode;
import com.intellij.lang.ParserDefinition;
import com.intellij.lang.PsiParser;
import com.intellij.lexer.Lexer;
import com.intellij.openapi.project.Project;
import com.intellij.psi.FileViewProvider;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.IFileElementType;
import com.intellij.psi.tree.TokenSet;
import org.jetbrains.annotations.NotNull;

public class CLParserDefinition implements ParserDefinition {
    @NotNull
    public Lexer createLexer(Project project) {
        return new CLParsingLexer();
    }

    public PsiParser createParser(Project project) {
        return new CLParser();
    }

    public IFileElementType getFileNodeType() {
        return CLElementTypes.FILE;
    }

    @NotNull
    public TokenSet getWhitespaceTokens() {
        return TokenSet.create(CLTokenTypes.WHITESPACE, CLTokenTypes.EOL, CLTokenTypes.EOF);
    }

    @NotNull
    public TokenSet getCommentTokens() {
        return CLTokenTypes.COMMENTS;
    }

    public PsiFile createFile(FileViewProvider viewProvider) {
        return new CLFile(viewProvider);
    }

    public SpaceRequirements spaceExistanceTypeBetweenTokens(ASTNode left, ASTNode right) {
        if (left.getElementType() == CLTokenTypes.COMMA
                || left.getElementType() == CLTokenTypes.COMMA_AT
                || left.getElementType() == CLTokenTypes.BACKQUOTE
                || left.getElementType() == CLTokenTypes.SHARP_QUOTE) {
            return SpaceRequirements.MUST_NOT;
        } else if (left.getElementType() == CLTokenTypes.LEFT_PAREN
                || right.getElementType() == CLTokenTypes.RIGHT_PAREN
                || left.getElementType() == CLTokenTypes.RIGHT_PAREN
                || right.getElementType() == CLTokenTypes.LEFT_PAREN) {
            // TODO - Is this right? Are there spacing requirements in Lisp that I'm not aware of?
            return SpaceRequirements.MAY;
        }
        return SpaceRequirements.MUST;
    }

    @NotNull
    public PsiElement createElement(ASTNode node) {
        final IElementType type = node.getElementType();
        if (type == CLElementTypes.FILE) {
            return new CLRoot(node);
        } else if (type == CLElementTypes.FUNCTION_DEFINITION) {
            return new CLDefun(node);
        } else if (type == CLElementTypes.MACRO_DEFINITION) {
            return new CLDefmacro(node);
        } else if (type == CLElementTypes.FORMAL_PARAMETER_LIST) {
            return new CLFormalParameterList(node);
        } else if (type == CLElementTypes.REGULAR_PARAMETER) {
            return new CLFormalParameter(node);
        } else if (type == CLElementTypes.FUNCTION_CALL) {
            return new CLFunctionCall(node);
        } else if (type == CLElementTypes.GLOBAL_CONSTANT_DEFINITION) {
            return new CLDefvar(node);
        } else if (type == CLElementTypes.GLOBAL_VARIABLE_DEFINITION) {
            return new CLDefvar(node);
        } else if (type == CLElementTypes.BACKQUOTED_EXPRESSION) {
            return new CLBackquotedExpression(node);
        }

        // TODO - "Key" formal parameters
        // TODO - "Optional" formal parameters

        // TODO - COND blocks

        // TODO - IF blocks

        // TODO - DO, DOWHILE, LOOP blocks

        // TODO - LET statements (local variables) - BTW, anything smart we should do with closures?

        // TODO - LAMBDA statements (should these just be regular function definitions?)

        return new ASTWrapperPsiElement(node);
    }
}